#!/usr/bin/env python3
"""
RedFire 开发环境启动脚本 (Python版)
=====================================

用于在开发环境中启动RedFire系统的核心服务:
- API Gateway (端口: 8000)
- User Service (端口: 8002) 
- Market Data Service (端口: 8004)
- 前端应用 (端口: 3001)

支持 Windows/Linux/macOS 跨平台使用
"""

import os
import sys
import time
import signal
import socket
import subprocess
import threading
import argparse
from pathlib import Path
from typing import List, Dict, Optional
import json

# 颜色定义 (支持Windows)
class Colors:
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    YELLOW = '\033[1;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    NC = '\033[0m'  # No Color
    
    @classmethod
    def disable_on_windows(cls):
        """在Windows上禁用颜色（如果需要）"""
        if os.name == 'nt' and not os.environ.get('FORCE_COLOR'):
            cls.RED = cls.GREEN = cls.YELLOW = cls.BLUE = cls.PURPLE = cls.CYAN = cls.NC = ''

# 全局变量
PROJECT_ROOT = Path(__file__).parent.absolute()
PROCESSES: List[subprocess.Popen] = []
SERVICES_STARTED = 0

# 添加项目根目录到Python路径，支持适配器模块
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

def print_message(color: str, message: str):
    """打印带颜色的消息"""
    print(f"{color}{message}{Colors.NC}")

# 导入适配器
try:
    from shared.adapters.unified_config import UnifiedConfig
    from shared.adapters.dependency_resolver import DependencyResolver
    ADAPTERS_AVAILABLE = True
    print_message(Colors.GREEN, "✅ 适配器模块加载成功")
except ImportError as e:
    print_message(Colors.YELLOW, f"⚠️ 适配器模块不可用: {e}")
    ADAPTERS_AVAILABLE = False

def print_banner():
    """打印启动横幅"""
    print()
    print_message(Colors.CYAN, "🔥 RedFire 开发环境启动脚本 (Python版)")
    print_message(Colors.CYAN, "==========================================")
    print()

def check_command(cmd: str) -> bool:
    """检查命令是否存在"""
    try:
        # 在Windows上，使用shell=True以支持.cmd和.bat文件
        result = subprocess.run([cmd, '--version'], 
                      capture_output=True, 
                      check=False, 
                      timeout=5,
                      shell=True if sys.platform == 'win32' else False)
        return result.returncode == 0
    except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError):
        return False

def check_port(port: int) -> bool:
    """检查端口是否可用"""
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(1)
            result = s.connect_ex(('localhost', port))
            return result != 0  # 0 表示连接成功（端口被占用）
    except socket.error:
        return True  # 假设端口可用

def wait_for_port(port: int, service_name: str, timeout: int = 30) -> bool:
    """等待端口就绪"""
    print_message(Colors.YELLOW, f"⏳ 等待 {service_name} (端口 {port}) 启动...")
    
    for i in range(timeout):
        if not check_port(port):  # 端口被占用说明服务启动了
            print_message(Colors.GREEN, f"✅ {service_name} 启动成功 (端口: {port})")
            return True
        
        # 每10秒提示一次进度（仅针对长时间等待）
        if timeout > 60 and (i + 1) % 10 == 0 and (i + 1) < timeout:
            remaining = timeout - (i + 1)
            print_message(Colors.CYAN, f"   等待中... ({i + 1}s / {timeout}s, 剩余 {remaining}s)")
        
        time.sleep(1)
    
    print_message(Colors.RED, f"❌ {service_name} 启动超时")
    return False

def get_python_executable() -> str:
    """获取Python可执行文件路径"""
    # 优先使用当前Python解释器
    python_exe = sys.executable
    if python_exe and os.path.exists(python_exe):
        return python_exe
    
    # 备选方案
    candidates = ['python3', 'python', 'py']
    for cmd in candidates:
        if check_command(cmd):
            return cmd
    
    raise RuntimeError("未找到可用的Python解释器")

def start_backend_service(service_name: str, service_path: str, port: int, description: str) -> bool:
    """启动后端服务"""
    global SERVICES_STARTED
    
    print_message(Colors.BLUE, f"🚀 启动 {description}...")
    
    # 检查端口是否被占用
    if not check_port(port):
        print_message(Colors.YELLOW, f"⚠️  端口 {port} 已被占用，跳过启动 {service_name}")
        return False
    
    # 创建日志目录
    logs_dir = PROJECT_ROOT / "logs"
    logs_dir.mkdir(exist_ok=True)
    
    # 准备启动命令 - 使用uvicorn而不是直接运行main.py
    python_exe = get_python_executable()
    
    # 根据服务类型使用不同的启动方式
    if service_name == "user_service":
        module_path = "backend.services.user_service.app.main:app"
    elif service_name == "api_gateway":
        # API网关目录名包含连字符，使用字符串形式路径
        module_path = "backend.services.api-gateway.app.main:app"
    elif service_name == "market_data_service":
        module_path = "backend.services.market_data_service.app.main:app"
    else:
        # 备用方案：检查服务文件是否存在
        full_path = PROJECT_ROOT / service_path
        if not full_path.exists():
            print_message(Colors.RED, f"❌ 服务文件不存在: {service_path}")
            return False
        cmd = [python_exe, str(full_path)]
        module_path = None
    
    if module_path:
        # 使用uvicorn启动
        cmd = [
            python_exe, "-m", "uvicorn", module_path,
            "--reload",
            "--host", "0.0.0.0",
            "--port", str(port)
        ]
    
    # 设置环境变量
    env = os.environ.copy()
    env.update({
        'PYTHONPATH': str(PROJECT_ROOT),
        'PYTHONUNBUFFERED': '1',
        'REDFIRE_ENV': 'development',
        'REDFIRE_LOG_LEVEL': 'INFO',
        'REDFIRE_SERVICE_NAME': service_name,
        'REDFIRE_SERVICE_PORT': str(port),
        'REDFIRE_ADAPTERS_ENABLED': str(ADAPTERS_AVAILABLE).lower()
    })
    
    # 如果适配器可用，添加适配器配置
    if ADAPTERS_AVAILABLE:
        try:
            db_config = UnifiedConfig.get_database_config()
            env.update({
                'REDFIRE_DB_HOST': db_config.get('host', 'localhost'),
                'REDFIRE_DB_PORT': str(db_config.get('port', 3306)),
                'REDFIRE_DB_NAME': db_config.get('database', 'redfire'),
                'REDFIRE_DB_USER': db_config.get('username', 'redfire')
            })
            
            redis_config = UnifiedConfig.get_redis_config()
            env.update({
                'REDFIRE_REDIS_HOST': redis_config.get('host', 'localhost'),
                'REDFIRE_REDIS_PORT': str(redis_config.get('port', 6379)),
                'REDFIRE_REDIS_DB': str(redis_config.get('db', 0))
            })
            print_message(Colors.CYAN, f"  📍 使用适配器配置启动 {service_name}")
        except Exception as e:
            print_message(Colors.YELLOW, f"  ⚠️ 设置适配器环境变量失败: {e}")
    
    try:
        # 启动服务
        log_file = logs_dir / f"{service_name}.log"
        with open(log_file, 'w', encoding='utf-8') as log:
            process = subprocess.Popen(
                cmd,
                cwd=PROJECT_ROOT,
                env=env,
                stdout=log,
                stderr=subprocess.STDOUT,
                text=True
            )
        
        PROCESSES.append(process)
        print_message(Colors.CYAN, f"📝 {service_name} PID: {process.pid}")
        
        # 等待服务启动
        if wait_for_port(port, description, 30):
            SERVICES_STARTED += 1
            return True
        else:
            return False
            
    except Exception as e:
        print_message(Colors.RED, f"❌ 启动 {service_name} 失败: {e}")
        return False

def find_package_manager() -> str:
    """寻找可用的包管理器"""
    frontend_dir = PROJECT_ROOT / "frontend"
    
    # 优先读取 package.json 中的 packageManager 字段
    package_json_path = frontend_dir / "package.json"
    if package_json_path.exists():
        try:
            import json
            with open(package_json_path, 'r', encoding='utf-8') as f:
                package_json = json.load(f)
                if 'packageManager' in package_json:
                    # packageManager 格式: "npm@9.8.1" 或 "pnpm@8.0.0"
                    manager = package_json['packageManager'].split('@')[0]
                    if manager in ['npm', 'pnpm', 'yarn'] and check_command(manager):
                        print_message(Colors.CYAN, f"📦 使用 package.json 中指定的包管理器: {manager}")
                        return manager
        except Exception as e:
            print_message(Colors.YELLOW, f"⚠️ 读取 package.json 失败: {e}")
    
    # 检查是否有pnpm配置
    if (PROJECT_ROOT / "pnpm-workspace.yaml").exists() or (frontend_dir / "pnpm-workspace.yaml").exists():
        if check_command('pnpm'):
            print_message(Colors.CYAN, "📦 检测到 pnpm-workspace.yaml，使用 pnpm")
            return 'pnpm'
    
    # 备选npm
    if check_command('npm'):
        print_message(Colors.CYAN, "📦 使用默认包管理器: npm")
        return 'npm'
    
    raise RuntimeError("未找到可用的包管理器 (npm/pnpm)")

def start_frontend() -> bool:
    """启动前端应用"""
    global SERVICES_STARTED
    
    print_message(Colors.BLUE, "🚀 启动前端应用...")
    
    frontend_dir = PROJECT_ROOT / "frontend"
    
    if not frontend_dir.exists():
        print_message(Colors.YELLOW, "⚠️  前端目录不存在，跳过前端启动")
        return False
    
    # 检查端口是否被占用 (前端使用3001端口)
    if not check_port(3001):
        print_message(Colors.YELLOW, "⚠️  端口 3001 已被占用，跳过前端启动")
        return False
    
    try:
        package_manager = find_package_manager()
    except RuntimeError as e:
        print_message(Colors.RED, f"❌ {e}")
        return False
    
    # 检查依赖是否安装
    node_modules = frontend_dir / "node_modules"
    if not node_modules.exists():
        print_message(Colors.YELLOW, "📦 安装前端依赖...")
        try:
            subprocess.run([package_manager, 'install'], 
                         cwd=frontend_dir, 
                         check=True,
                         timeout=300,  # 5分钟超时
                         shell=(sys.platform == 'win32'))  # Windows上需要shell=True
        except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
            print_message(Colors.RED, f"❌ 安装前端依赖失败: {e}")
            return False
    
    # 创建日志目录
    logs_dir = PROJECT_ROOT / "logs"
    logs_dir.mkdir(exist_ok=True)
    
    try:
        # 启动前端开发服务器 (使用Turborepo的web-app)
        log_file = logs_dir / "frontend.log"
        with open(log_file, 'w', encoding='utf-8') as log:
            # Windows上需要shell=True来运行npm命令
            process = subprocess.Popen(
                [package_manager, 'run', 'dev:web'],
                cwd=frontend_dir,
                stdout=log,
                stderr=subprocess.STDOUT,
                text=True,
                shell=(sys.platform == 'win32')
            )
        
        PROCESSES.append(process)
        print_message(Colors.CYAN, f"📝 Frontend PID: {process.pid}")
        
        # 等待前端启动（前端需要编译，时间较长）
        if wait_for_port(3001, "前端应用", 120):
            SERVICES_STARTED += 1
            return True
        else:
            # 前端启动失败，但不影响整体启动
            print_message(Colors.YELLOW, "⚠️  前端可能仍在启动中，请稍后访问 http://localhost:3001")
            return False
            
    except Exception as e:
        print_message(Colors.RED, f"❌ 启动前端失败: {e}")
        return False

def cleanup():
    """清理函数"""
    print_message(Colors.YELLOW, "🛑 正在停止所有服务...")
    
    for process in PROCESSES:
        if process.poll() is None:  # 进程还在运行
            try:
                print_message(Colors.CYAN, f"🛑 停止进程 PID: {process.pid}")
                process.terminate()
            except Exception as e:
                print_message(Colors.YELLOW, f"⚠️  停止进程失败: {e}")
    
    # 等待进程结束
    time.sleep(2)
    
    # 强制结束仍在运行的进程
    for process in PROCESSES:
        if process.poll() is None:
            try:
                print_message(Colors.YELLOW, f"⚠️  强制结束进程 PID: {process.pid}")
                process.kill()
            except Exception as e:
                print_message(Colors.RED, f"❌ 强制结束进程失败: {e}")
    
    print_message(Colors.GREEN, "✅ 所有服务已停止")

def signal_handler(signum, frame):
    """信号处理器"""
    print_message(Colors.YELLOW, "\n🛑 接收到停止信号...")
    cleanup()
    sys.exit(0)

def check_environment():
    """环境检查"""
    print_message(Colors.BLUE, "🔍 检查环境...")
    
    # 检查Python
    try:
        python_exe = get_python_executable()
        print_message(Colors.GREEN, f"✅ Python: {python_exe}")
    except RuntimeError:
        print_message(Colors.RED, "❌ 请安装 Python 3.8+")
        return False
    
    # 检查uvicorn
    try:
        result = subprocess.run([python_exe, "-m", "uvicorn", "--version"], 
                              capture_output=True, check=False, timeout=5)
        if result.returncode == 0:
            print_message(Colors.GREEN, "✅ Uvicorn: 可用")
        else:
            print_message(Colors.YELLOW, "⚠️  Uvicorn 未安装，尝试安装...")
            subprocess.run([python_exe, "-m", "pip", "install", "uvicorn[standard]"], 
                         check=True, timeout=60)
            print_message(Colors.GREEN, "✅ Uvicorn: 安装成功")
    except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
        print_message(Colors.RED, "❌ 请安装 uvicorn: pip install uvicorn[standard]")
        return False
    
    # 检查Node.js (可选)
    if check_command('node'):
        print_message(Colors.GREEN, "✅ Node.js: 可用")
    else:
        print_message(Colors.YELLOW, "⚠️  Node.js 未安装，将跳过前端启动")
    
    # 检查基础设施服务
    check_infrastructure_services()
    
    return True

def check_infrastructure_services():
    """检查基础设施服务（MySQL、Redis等）"""
    print_message(Colors.BLUE, "🔍 检查基础设施服务...")
    
    # 检查MySQL
    mysql_ok = check_mysql_service()
    
    # 检查Redis  
    redis_ok = check_redis_service()
    
    if mysql_ok and redis_ok:
        print_message(Colors.GREEN, "✅ 基础设施服务全部可用")
    elif mysql_ok or redis_ok:
        print_message(Colors.YELLOW, "⚠️ 部分基础设施服务可用，将使用降级模式")
    else:
        print_message(Colors.RED, "❌ 基础设施服务不可用，将使用模拟模式")

def check_mysql_service():
    """检查MySQL服务"""
    try:
        if ADAPTERS_AVAILABLE:
            config = UnifiedConfig.get_database_config()
            host = config.get('host', 'localhost')
            port = config.get('port', 3306)
        else:
            host = 'localhost'
            port = 3306
        
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(3)
        result = sock.connect_ex((host, port))
        sock.close()
        
        if result == 0:
            print_message(Colors.GREEN, f"  ✅ MySQL ({host}:{port}): 连接正常")
            return True
        else:
            print_message(Colors.YELLOW, f"  ⚠️ MySQL ({host}:{port}): 连接失败")
            return False
            
    except Exception as e:
        print_message(Colors.YELLOW, f"  ⚠️ MySQL检查异常: {e}")
        return False

def check_redis_service():
    """检查Redis服务"""
    try:
        if ADAPTERS_AVAILABLE:
            config = UnifiedConfig.get_redis_config()
            host = config.get('host', 'localhost')  
            port = config.get('port', 6379)
        else:
            host = 'localhost'
            port = 6379
        
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(3)
        result = sock.connect_ex((host, port))
        sock.close()
        
        if result == 0:
            print_message(Colors.GREEN, f"  ✅ Redis ({host}:{port}): 连接正常")
            return True
        else:
            print_message(Colors.YELLOW, f"  ⚠️ Redis ({host}:{port}): 连接失败")
            return False
            
    except Exception as e:
        print_message(Colors.YELLOW, f"  ⚠️ Redis检查异常: {e}")
        return False

def verify_demo_api(gateway_port: int = 8000) -> bool:
    """
    验证演示API可用性
    
    Args:
        gateway_port: API网关端口
        
    Returns:
        bool: 是否可用
    """
    try:
        import requests
        
        print_message(Colors.CYAN, "=" * 70)
        print_message(Colors.CYAN, "📊 验证市场数据演示API")
        print_message(Colors.CYAN, "=" * 70)
        
        url = f"http://localhost:{gateway_port}/api/v1/market/demo/index-bars"
        params = {"days": 7, "limit": 10, "frequency": "1d"}
        
        print_message(Colors.YELLOW, f"🔍 正在测试: {url}")
        print_message(Colors.YELLOW, f"   参数: days=7, limit=10, frequency=1d")
        
        response = requests.get(url, params=params, timeout=10)
        
        if response.status_code == 200:
            data = response.json()
            if data.get("success"):
                print_message(Colors.GREEN, "✅ 演示API测试成功!")
                count = data.get("data", {}).get("statistics", {}).get("count", 0)
                symbol = data.get("data", {}).get("symbol", "N/A")
                print_message(Colors.GREEN, f"   成功获取 {count} 条K线数据 ({symbol})")
                print()
                print_message(Colors.CYAN, "📈 市场数据演示页面:")
                print_message(Colors.CYAN, "   http://localhost:3001/demo")
                print_message(Colors.CYAN, "   (支持参数调整、实时刷新、K线图表)")
                print()
                print_message(Colors.CYAN, "=" * 70)
                return True
            else:
                print_message(Colors.YELLOW, f"⚠️  API返回失败: {data.get('message', 'Unknown error')}")
        else:
            print_message(Colors.YELLOW, f"⚠️  HTTP错误: {response.status_code}")
            
    except requests.exceptions.Timeout:
        print_message(Colors.YELLOW, "⚠️  API请求超时")
    except requests.exceptions.ConnectionError:
        print_message(Colors.YELLOW, "⚠️  无法连接到API网关")
    except Exception as e:
        print_message(Colors.YELLOW, f"⚠️  API验证异常: {e}")
    
    print_message(Colors.YELLOW, "   演示API暂时不可用，但服务已启动")
    print_message(Colors.YELLOW, "   可稍后访问: http://localhost:3001/demo")
    print()
    print_message(Colors.CYAN, "=" * 70)
    return False


def monitor_services():
    """监控服务状态"""
    while True:
        running_count = 0
        for process in PROCESSES:
            if process.poll() is None:
                running_count += 1
        
        if running_count == 0:
            print_message(Colors.RED, "❌ 所有服务都已停止")
            break
        
        time.sleep(5)

def main():
    """主函数"""
    global SERVICES_STARTED
    
    # 在Windows上可能需要禁用颜色
    if os.name == 'nt':
        Colors.disable_on_windows()
    
    parser = argparse.ArgumentParser(description='RedFire 开发环境启动脚本')
    parser.add_argument('--no-frontend', action='store_true', help='不启动前端应用')
    parser.add_argument('--port-api', type=int, default=8000, help='API网关端口 (默认: 8000)')
    parser.add_argument('--port-user', type=int, default=8002, help='用户服务端口 (默认: 8002)')
    parser.add_argument('--port-market', type=int, default=8004, help='市场数据服务端口 (默认: 8004)')
    
    args = parser.parse_args()
    
    print_banner()
    
    # 设置信号处理
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    
    # 环境检查
    if not check_environment():
        sys.exit(1)
    
    # 创建日志目录
    logs_dir = PROJECT_ROOT / "logs"
    logs_dir.mkdir(exist_ok=True)
    
    # 启动服务
    print_message(Colors.BLUE, "🚀 启动RedFire开发环境...")
    
    # 启动后端核心服务
    start_backend_service(
        "user_service", 
        "backend/services/user_service/main.py", 
        args.port_user, 
        "用户服务"
    )
    time.sleep(2)
    
    start_backend_service(
        "api_gateway", 
        "backend/services/api-gateway/app/main.py", 
        args.port_api, 
        "API网关"
    )
    time.sleep(2)
    
    start_backend_service(
        "market_data_service", 
        "backend/services/market_data_service/app/main.py", 
        args.port_market, 
        "市场数据服务"
    )
    time.sleep(2)
    
    # 启动前端 (如果需要)
    if not args.no_frontend and check_command('node'):
        start_frontend()
    
    # 启动完成
    if SERVICES_STARTED > 0:
        print()
        print_message(Colors.GREEN, "🎉 RedFire开发环境启动完成!")
        print()
        print_message(Colors.CYAN, "📋 服务访问地址:")
        print_message(Colors.CYAN, f"  🌐 前端应用:     http://localhost:3001")
        print_message(Colors.CYAN, f"  🚪 API网关:      http://localhost:{args.port_api}")
        print_message(Colors.CYAN, f"  👥 用户服务:     http://localhost:{args.port_user}")
        print_message(Colors.CYAN, f"  📊 市场数据服务: http://localhost:{args.port_market}")
        print()
        print_message(Colors.CYAN, f"📝 日志文件位置: {logs_dir}/")
        print()
        
        # 验证演示API可用性
        verify_demo_api(args.port_api)
        
        print_message(Colors.YELLOW, "按 Ctrl+C 停止所有服务")
        print()
        
        # 保持脚本运行并监控服务
        try:
            monitor_services()
        except KeyboardInterrupt:
            pass
        finally:
            cleanup()
    else:
        print_message(Colors.RED, "❌ 没有服务成功启动")
        sys.exit(1)

if __name__ == "__main__":
    # 检查是否以管理员权限运行 (Windows)
    if os.name == 'nt':
        try:
            import ctypes
            if ctypes.windll.shell32.IsUserAnAdmin():
                print_message(Colors.YELLOW, "⚠️  不建议以管理员权限运行此脚本")
        except:
            pass
    
    # 检查是否以root权限运行 (Unix)
    elif os.getuid() == 0:
        print_message(Colors.YELLOW, "⚠️  不建议以root权限运行此脚本")
    
    main()
